75장. SNS — 팬아웃 패턴
이 장에서 말하고자 하는 것
SQS는 한 큐에 한 Consumer 그룹이 붙는 구조다.
한 메시지 → 한 그룹이 처리
그런데 같은 메시지를 여러 곳에 동시에 보내야 할 때가 있다.
"주문 생성됨"
→ 알림 서비스도 알아야 하고
→ 분석 서비스도 알아야 하고
→ 추천 서비스도 알아야 하고
→ 재고 서비스도 알아야 한다
이때 등장하는 게
Amazon SNS (Simple Notification Service)
다.
1. SNS의 기본 모델 — 1:N
[Publisher]
↓ Publish
[Topic: order-events]
↓ 동일 메시지를 모든 구독자에게 전달
├─ Subscriber 1 (SQS)
├─ Subscriber 2 (SQS)
├─ Subscriber 3 (Lambda)
└─ Subscriber 4 (HTTPS endpoint)
- 토픽에 한 번 발행하면 모든 구독자에게 복제 전달
- Publisher는 누가 받는지 모른다
- 구독자는 자유롭게 추가/삭제
2. Fan-out 패턴 — SNS + SQS 조합
가장 흔한 운영 패턴이다.
[Publisher]
↓
[SNS Topic]
├─ SQS: notification-jobs → notification worker
├─ SQS: analytics-jobs → analytics worker
├─ SQS: recommendation-jobs → recommendation worker
└─ SQS: inventory-jobs → inventory worker
- 발행자는 SNS에 한 번
- 각 Worker는 자기 큐에서 자기 페이스로 처리
- 한쪽이 막혀도 다른 큐에는 영향 없음
- 새 구독자를 추가해도 발행자 코드는 변경 없음
SNS 직접 → Lambda / HTTPS 도 가능하지만 SNS → SQS → Worker가 안정적
3. 메시지 필터링 — 받고 싶은 것만 받기
SNS는 구독에 필터 정책 을 둘 수 있다.
구독자가 받고 싶은 것:
{ "type": ["OrderCreated", "OrderCancelled"] }
토픽에 흘러오는 메시지:
- OrderCreated → 통과
- OrderShipped → 차단
- OrderCancelled → 통과
필터링은 SNS 단계에서 일어나므로 불필요한 트래픽이 SQS까지 가지 않는다.
4. Order는 보장하지 않는다 (기본)
SNS Standard 토픽은 순서 보장이 없다.
"OrderCreated" → "OrderPaid" 발행
↓
구독자는 "OrderPaid" 가 먼저 도착할 수도 있다
순서가 중요한 도메인은 SNS FIFO + SQS FIFO 조합으로 묶는다.
5. SNS와 EventBridge — 비슷한데 다르다
“둘 다 1:N 인데 뭐가 다른가?”
이 질문이 자주 나온다.
SNS → 단순 팬아웃 (빠름, 저비용)
EventBridge → 풍부한 라우팅, 서드파티 통합, 스키마 레지스트리
- 내부 단순 fan-out → SNS
- 복잡한 라우팅 / 서드파티 SaaS · AWS 서비스 이벤트 통합 → EventBridge
다음 76장에서 자세히 본다.
6. 우리 서비스에서
[orders 서비스]
↓ Publish "OrderCreated"
[SNS Topic: order-events]
├─ SQS: notification-jobs → ECS notification worker → 이메일/푸시
├─ SQS: analytics-jobs → ECS analytics worker → DynamoDB
└─ SQS: inventory-jobs → ECS inventory worker → 재고 차감
- orders 서비스 코드는 단 한 줄:
sns.publish(...) - 새 도메인(예: loyalty) 이 생기면 그쪽 SQS를 토픽에 구독시키면 끝
7. 직접 확인해보기 — CLI
토픽 만들기
aws sns create-topic --name order-events
구독 (SQS)
aws sns subscribe \
--topic-arn <topic-arn> \
--protocol sqs \
--notification-endpoint <sqs-queue-arn>
발행
aws sns publish \
--topic-arn <topic-arn> \
--message '{"type":"OrderCreated","orderId":"o-1"}' \
--message-attributes '{
"type": {"DataType":"String","StringValue":"OrderCreated"}
}'
message-attributes 가 필터 정책의 기준이 된다.
8. 코드로는 이렇게 생겼다 — Terraform
resource "aws_sns_topic" "order_events" {
name = "order-events"
}
resource "aws_sqs_queue" "notification_jobs" {
name = "notification-jobs"
}
resource "aws_sns_topic_subscription" "notification" {
topic_arn = aws_sns_topic.order_events.arn
protocol = "sqs"
endpoint = aws_sqs_queue.notification_jobs.arn
raw_message_delivery = true
filter_policy = jsonencode({
type = ["OrderCreated", "OrderCancelled"]
})
}
# SNS가 SQS에 쓸 수 있도록 큐 정책
resource "aws_sqs_queue_policy" "notification" {
queue_url = aws_sqs_queue.notification_jobs.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = { Service = "sns.amazonaws.com" }
Action = "sqs:SendMessage"
Resource = aws_sqs_queue.notification_jobs.arn
Condition = {
ArnEquals = { "aws:SourceArn" = aws_sns_topic.order_events.arn }
}
}]
})
}
SNS → SQS의 권한 설정이 자주 까먹는 부분이다.
9. 이렇게 쓰면 망한다 — 안티패턴
안티패턴 1. SNS → 여러 Lambda 직접 구독
Lambda 가 한 번이라도 실패하면 메시지 손실 가능 (재시도는 있지만 한계).
안정성이 중요하면 SNS → SQS → Lambda
안티패턴 2. 한 큐에 여러 도메인 메시지를 섞는다
필터 없이 모든 메시지가 한 큐에 → 워커가 자기와 무관한 메시지까지 처리.
도메인별 큐를 분리하고 SNS 필터로 분기
안티패턴 3. 메시지 본문에 거대한 페이로드를 넣는다
SNS · SQS는 메시지 크기 제한이 있다 (기본 256KB).
큰 데이터는 S3에 두고 SNS에는 키만.
안티패턴 4. 발행자가 누가 받는지 신경 쓴다
SNS의 핵심은 발행자/구독자 분리.
발행자가 구독자 추가/제거를 알아야 한다면 추상화가 무너진 것.
10. 한 줄로 정리
SNS는 1:N 팬아웃 도구이며,
“SNS → 여러 SQS → 각자 워커” 가 가장 안정적인 패턴이다
11. 이 장의 핵심 정리
- SNS는 토픽 기반의 1:N 발행/구독 모델이다.
- 발행자는 구독자를 모른다 — 결합도가 결정적으로 낮아진다.
- SNS → SQS → Worker 가 가장 안정적인 운영 패턴이다.
- 필터 정책으로 구독자가 받고 싶은 것만 받는다.
- 순서가 중요하면 SNS FIFO + SQS FIFO.
- 단순 팬아웃은 SNS, 복잡한 라우팅은 EventBridge.